/**
 * 获取元素的边界信息
 * 仿照@vueuse/core的useElementBounding
 */
import React, { useState, useEffect } from 'react'

type optionType = {
  reset?: boolean
  windowResize?: boolean
  windowScroll?: boolean
  immediate?: boolean
  updateTiming?: 'sync' | 'next-frame'
}

// 自定义 Hook：useElementBounding
export const useElementBounding = (
  elementRef: React.RefObject<HTMLElement>,
  options: optionType = {}
) => {
  const {
    reset = true,
    windowResize = true,
    windowScroll = true,
    immediate = true,
    updateTiming = 'sync'
  } = options

  const [bounding, setBounding] = useState({
    width: 0,
    height: 0,
    top: 0,
    left: 0,
    right: 0,
    bottom: 0,
    x: 0,
    y: 0
  })

  const recalculate = (el: HTMLElement) => {
    const rect = el.getBoundingClientRect()
    setBounding({
      width: rect.width,
      height: rect.height,
      top: rect.top,
      left: rect.left,
      right: rect.right,
      bottom: rect.bottom,
      x: rect.x,
      y: rect.y
    })
  }

  const updateBounding = (element: HTMLElement) => {
    if (updateTiming === 'sync') {
      recalculate(element)
    } else {
      requestAnimationFrame(() => recalculate(element))
    }
  }

  useEffect(() => {
    const element = elementRef.current
    if (!element) return
    !element && updateBounding(element)
  }, [elementRef])

  useEffect(() => {
    const el = elementRef.current
    if (!el) {
      if (reset) {
        setBounding({
          width: 0,
          height: 0,
          top: 0,
          left: 0,
          right: 0,
          bottom: 0,
          x: 0,
          y: 0
        })
      }
      return
    }

    if (immediate) updateBounding(el)

    // 使用 ResizeObserver 监听元素尺寸变化
    const observer = new ResizeObserver(() => updateBounding(el))
    observer.observe(el)

    const config = { attributeFilter: ['style', 'class'] }
    const mobserver = new MutationObserver(() => updateBounding(el))
    mobserver.observe(el, config)

    // 监听窗口滚动和大小调整
    if (windowScroll) {
      window.addEventListener('scroll', () => updateBounding(el), { passive: true, capture: true })
    }
    if (windowResize) {
      window.addEventListener('resize', () => updateBounding(el))
    }
    // 清理函数
    return () => {
      observer.unobserve(el)
      mobserver.disconnect()
      windowScroll && window.removeEventListener('scroll', () => updateBounding(el))
      windowResize && window.removeEventListener('resize', () => updateBounding(el))
    }
  }, [])

  return bounding
}

export type UseElementBoundingReturn = ReturnType<typeof useElementBounding>
